// +----------------------------------------------------------------------
// | CodeEngine
// +----------------------------------------------------------------------
// | Copyright 艾邦
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: TaoGe <liangtao.gz@foxmail.com>
// +----------------------------------------------------------------------
// | Version: 2.0 2019-09-07 15:36
// +----------------------------------------------------------------------
'use strict';

import CryptoJS from 'crypto-js';
import Tool from './tool';

export default class Interface {

	constructor( config, storage, helper ) {
		if ( Tool.empty( config ) ) {
			throw new SyntaxError( 'Interface实例化config不能为空' )
		}
		if ( Tool.empty( storage ) ) {
			throw new SyntaxError( 'Interface实例化storage不能为空' )
		}
		if ( Tool.empty( helper ) ) {
			throw new SyntaxError( 'Interface实例化helper不能为空' )
		}
		this.config = config;
		this.helper = helper;
		this.storage = storage;
	}

	static getInstance( config, storage, helper ) {
		if ( !this.instance ) {
			this.instance = new Interface( config, storage, helper );
		}
		return this.instance;
	}

	/**
	 * 数据格式化
	 * @param    {object}  params    需要格式化的数据
	 * @returns  {string}
	 */
	formatData( params ) {
		let options = {};
		if ( params !== undefined ) {
			options = params;
		}
		return this.formatJson( options );
	}

	/**
	 * 生成STATE防止CSRF攻击
	 * @param params
	 * @returns {string}
	 */
	createState( params ) {
		if ( this.config.state === false ) {
			return '';
		}
		const key = Tool.guid();
		const value = Tool.encode( JSON.stringify( { time: new Date().getTime(), data: params } ) );
		this.storage.set( key, value );
		return Tool.encode( key );
	}

	/**
	 * 验证STATE是否正确
	 * @param params
	 * @returns {boolean}
	 */
	checkState( params ) {
		if ( this.config.state === false ) {
			return true;
		}
		const key = Tool.decode( params.state );
		const value = this.storage.get( key );
		this.storage.del( key );
		if ( value === null ) {
			return false;
		}
		const data = JSON.parse( Tool.decode( value ) );
		if ( Tool.empty( data ) ) {
			return false;
		}
		if ( this.config.times_out > 0 ) { // 请求是否超时验证
			if ( !( typeof data.time === 'number' && !isNaN( data.time ) && ( ( new Date().getTime() - data.time ) / 1000 ) <= this.config.times_out ) ) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 生成签名
	 * @param params
	 * @returns {string}
	 */
	createSign( params ) {
		// 签名步骤一：生成待签名字符串
		const string_1 = this.createSignString( params );
		// 签名步骤二：在string后加入KEY
		const string_2 = string_1 + '&key=' + String( this.config.key );
		// 签名步骤三：MD5加密或者HMAC-SHA256
		let string_3 = '';
		if ( String( this.config.sign_type ).toUpperCase() === 'MD5' ) {
			string_3 = CryptoJS.MD5( string_2 ).toString();
		} else if ( String( this.config.sign_type ).toUpperCase() === 'HMAC-SHA256' || String( this.config.sign_type ).toUpperCase() === 'SHA256' ) {
			string_3 = CryptoJS.HmacSHA256( string_2, String( this.config.key ) ).toString()
		} else {
			throw new SyntaxError( '不支持的加密方式:' + String( this.config.sign_type ) )
		}
		// 签名步骤四：所有字符转为大写
		const str = String( string_3 ).toUpperCase();
		// 签名步骤五：Base64编码
		return Tool.encode( str )
	}

	/**
	 * 验证签名
	 * @param data
	 * @returns {boolean}
	 */
	checkSign( data ) {
		const signOld = data.sign;
		const signNew = this.createSign( data );
		return signOld === signNew
	}

	/**
	 * 生成待签名字符串
	 * @param data
	 * @returns {string}
	 */
	createSignString( data ) {
		delete data.sign;
		const appConfig = {
			app_id: String( this.config.app_id ),
			app_secret: String( this.config.app_secret )
		};
		const dataObj = this.filterAndSort( data );
		const appConfigObj = this.filterAndSort( appConfig );
		const str1 = this.objectToString( dataObj );
		const str2 = this.objectToString( appConfigObj );
		return str1 + '&' + str2;
	}

	/**
	 * 对象转字符串
	 * @param {object} $params  多维对象
	 * @returns {string|object}
	 */
	objectToString( $params ) {
		const that = this;
		if ( typeof ( $params ) === 'object' ) {
			let $buff = '';
			Object.keys( $params ).forEach( function ( $k ) {
				if ( typeof ( $params[ $k ] ) === 'object' ) {
					if ( $buff === '' ) {
						$buff += String( $k ) + '=' + that.objectToString( $params[ $k ] )
					} else {
						$buff += '&' + String( $k ) + '=' + that.objectToString( $params[ $k ] )
					}
				} else {
					if ( $buff === '' ) {
						$buff += String( $k ) + '=' + String( $params[ $k ] )
					} else {
						$buff += '&' + String( $k ) + '=' + String( $params[ $k ] )
					}
				}
			} );
			return $buff;
		}
		return $params;
	}

	/**
	 * 对象转json字符串
	 * @param params
	 * @returns {string}
	 */
	formatJson( params ) {
		const jsonString = { data: params, sign: '', state: '', timestamps: '' };
		if ( this.config.times_out > 0 ) {
			jsonString.timestamps = Date.parse( ( new Date() ).toString() ) / 1000;
		}
		if ( this.config.state ) {
			jsonString.state = this.createState( params );
		}
		if ( this.config.is_sign ) {
			jsonString.sign = this.createSign( jsonString );
		}
		return JSON.stringify( jsonString );
	}

	/**
	 * 筛选并排序
	 * @param params
	 * @returns {object}
	 */
	filterAndSort( params ) {
		const that = this;
		if ( typeof ( params ) === 'object' && JSON.stringify( params ) !== '{}' && params !== null ) {
			let $params = Tool.ksort( params );
			let $filterAndSort = {};
			Object.keys( $params ).forEach( function ( $k ) {
				if ( typeof ( $params[ $k ] ) === 'object' && JSON.stringify( $params[ $k ] ) !== '{}' && $params[ $k ] !== null ) {
					$filterAndSort[ $k ] = that.filterAndSort( $params[ $k ] );
				} else {
					if ( Tool.empty( $params[ $k ] ) === false ) {
						$filterAndSort[ $k ] = $params[ $k ];
					}
				}
			} );
			return Tool.ksort( $filterAndSort );
		} else {
			return params;
		}
	}
}

